package com.tmanager.persistence.dao;

import com.google.inject.Inject;
import com.google.inject.Provider;
import com.tmanager.persistence.entity.IdentifiedEntity;


import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;

/**
 * @author Paul A. Shmarev
 */
public abstract class BaseDAO<T extends IdentifiedEntity> implements DAO<T> {

    @Inject
    private final Provider<EntityManager> jpa = null;
    private final Class<T> entityClass;

    protected BaseDAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    protected EntityManager getEntManager() {
        return jpa.get();
    }

    protected Class<T> getEntityClass() {
        return entityClass;
    }

    @Override
    public T load(long id) {
        return jpa.get().find(entityClass, id);
    }

    @Override
    public T save(T entity) {
        return jpa.get().merge(entity);
    }

    @Override
    public void remove(T entity) {
        jpa.get().remove(entity);
    }

    @Override
    public void remove(long id) {
        T reference = jpa.get().getReference(getEntityClass(), id);
        jpa.get().remove(reference);
    }

    @Override
    public long count() {
        CriteriaBuilder builder = getEntManager().getCriteriaBuilder();
        CriteriaQuery<Long> query = builder.createQuery(Long.class);
        Root<T> from = query.from(getEntityClass());
        query.select(builder.count(from));
        TypedQuery<Long> typedQuery = getEntManager().createQuery(query);
        return typedQuery.getSingleResult();
    }

    @Override
    public List<T> findAll() {
        EntityManager em = jpa.get();
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<T> query = cb.createQuery(entityClass);
        TypedQuery<T> tq = em.createQuery(query.select(query.from(entityClass)));
        return tq.getResultList();
    }

    @Override
    public List<T> findAll(int first, int count) {
        EntityManager em = jpa.get();
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<T> query = cb.createQuery(entityClass);
        TypedQuery<T> tq = em.createQuery(query.select(query.from(entityClass)));
        tq.setFirstResult(first);
        tq.setMaxResults(count);
        return tq.getResultList();
    }

}
